﻿/*
 * ANSI.java
 *
 * Created on 2007年5月29日, 下午 4:52
 *
 */

package org.uranus.core.terminal;

import java.util.Enumeration;
import java.util.NoSuchElementException;
import java.util.Vector;
import org.uranus.core.dataHandler.HostInfoData;
import org.uranus.core.uitl.StringTokenizer;
import org.uranus.mainGUI.GUI;

/**
 * Copyright 2007 Hsiang-Yu Wang<br>
 * This file is distributed under the terms of the GNU General Public License<br>
 *<br>
 *<pre>
 * --------------------License notice--------------------
 * GNU GENERAL PUBLIC LICENSE
 * Version 3, 29 June 2007
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not,
 * visit: http://www.gnu.org/licenses/gpl.txt
 * --------------------License notice--------------------</pre>
 *
 * @author Hsiang-Yu Wang
 */
/**
 * 此class負責儲存所有byte資料, 以及處理所有ANSI控制碼
 */
public class ANSI {
    private TerminalFont font;
    private Vector show;
    private int bgcolor;
    private int fgcolor;
    private int type;
    
    private boolean isSSH = false;
    private int row;
    private int col;
    
    private int offset;
    
    private Terminal terminal;
    
    /** 將螢幕設計為24*80大小, 再將show裡的資訊如同填空格般填入陣列中 **/
    private TerminalFont[][] screen = new TerminalFont[24][80];
    
    private byte[] payload;
    /**
     * Creates a new instance of ANSI
     */
    
    private final byte ESC = 0x1B;
    private final byte Left_Bracket = 0x5B;
    private final byte CR = 0x0D;
    private final byte LF = 0x0A;
    
    
    public ANSI(){
        show = new Vector();
        bgcolor = 0x000000;
        fgcolor = 0xFFFFFF;
        type = 0;
        terminal = GUI.getCurrentTerminal();
    }
    
    public void setModeSSH(){
        isSSH = true;
        screen = new TerminalFont[300][80];
    }

    public void pushChars(char[] in){
        for(int i = 0 ; i < in.length ; i++){
            font = new TerminalFont(in[i]);
            show.addElement(font);
        }
        fillScreen();
    }
    
    
    public void startingFlush(byte[] in){
        try {
            payload = in;
            charset("Big5");
        } catch (ArrayIndexOutOfBoundsException ex) {
            ex.printStackTrace();
        } catch (NoSuchElementException ex) {
            ex.printStackTrace();
        }
    }
    
    public void clearBuffer(){
        offset = 0;
        payload = null;
        if(show != null)
            show.removeAllElements();
    }
    private void charset(String decoder) throws NoSuchElementException, ArrayIndexOutOfBoundsException{
        if(decoder.equals("Big5")){
            
            for(int i = 0 ; i < payload.length ;i++){
                /**先將big5字集過濾出來
                 * (根據Big5 2003規範所設計,則必須增加:(i.intValue() >= 0x8E && i.intValue() <= 0xA0) || (i.intValue() >= 0x81 && i.intValue() <= 0x8D)
                 * 因模擬器僅支援微軟950, 設計時暫時排除某些字元支援
                 * */
                if((payload[i] >= (byte)0xA1 && payload[i] <= (byte)0xF9) && (payload[i] != (byte)0xC7 && payload[i] != (byte)0xC8)){
                    if(i < payload.length -1 && (payload[i + 1] >= (byte)0x40 && payload[i + 1] <= (byte)0x7E)||( payload[i + 1] >= (byte)0xA1 && payload[i + 1] <= (byte)0xFE) ){
                        String s = new String(new byte[] {payload[i],payload[++i]});
                        font = new TerminalFont(s.charAt(0));
                        show.addElement(font);
                        
                        /** padding a null TerminalFont to make the cursor handler more easier to setup. **/
                        font = new TerminalFont((char)0);
                        font.setPadding(true);
                        show.addElement(font);
                        continue;
                    }else if( i < payload.length - 1){
                        /** Double Color on single chinese word **/
                        int temp = i; //save the first-half chinese word index.
                        
                        if( payload[i + 1] != (byte)0x1B){
                            font = new TerminalFont((char)0x1B);
                            show.addElement(font);
                            
                            font = new TerminalFont((char)payload[++i]);
                            show.addElement(font);
                        }else{
                            font = new TerminalFont((char)payload[++i]);
                            show.addElement(font);
                        }
                        
                        while(payload[i] != (byte)0x6D && i < payload.length - 1){
                            font = new TerminalFont((char)payload[++i]);
                            show.addElement(font);
                        }
                        
                        if( i == payload.length -1){
                            show.removeElementAt(show.size() - 1);
                            String s = new String(new byte[] {payload[temp], payload[i]});
                            font = new TerminalFont(s.charAt(0));
                        }else{
                            String s = new String(new byte[] {payload[temp],payload[++i]});
                            font = new TerminalFont(s.charAt(0));
                        }
                        
                        show.addElement(font);
                        /** padding a null TerminalFont to make the cursor handler more easier to setup. **/
                        font = new TerminalFont((char)0);
                        font.setPadding(true);
                        show.addElement(font);
                    }
                }else{
                    font = new TerminalFont((char)payload[i]);
                    show.addElement(font);
                }
            }
            
            fillScreen();
        }
    }
    
    public void fillScreen(){
        try{
            boolean check = true;
            boolean padding = false;
            show.trimToSize();
            for(Enumeration e = show.elements(); e.hasMoreElements();){
                
                font = (padding)? font : (TerminalFont)e.nextElement();
                padding = false;
                if((byte)font.getText() == ESC && !(font.isTwoBytes())){
                    String code = "";  // save the ANSI control code, reset needed.
                    font = (e.hasMoreElements())? (TerminalFont)e.nextElement() : null ;
                    
                    if(font != null && (byte)font.getText() == Left_Bracket){
                        check = true;
                        while(e.hasMoreElements() && check){
                            font = (TerminalFont)e.nextElement();
                            
                            switch(font.getText()){
                                case 'A':
                                    check = false;
                                    int offset = (code.length() == 0)? 1: Integer.parseInt(code);
                                    row -= offset;
                                    if(row < 0)
                                        row = 0;
                                    break;
                                case 'B':
                                    offset = (code.length() == 0)? 1: Integer.parseInt(code);
                                    row += offset;
                                    check = false;
                                    break;
                                case 'C':
                                    offset = (code.length() == 0)? 1: Integer.parseInt(code);
                                    col += offset;
                                    check = false;
                                    break;
                                case 'D':
                                    offset = (code.length() == 0)? 1: Integer.parseInt(code);
                                    col -= offset;
                                    if(col < 0)
                                        col = 0;
                                    check = false;
                                    break;
                                case 'H':
                                    check = false;
                                    cursorHome(code);
                                    break;
                                case 'J':
                                    eraseJ(code);
                                    check = false;
                                    break;
                                case 'K':
                                    eraseK(code);
                                    check = false;
                                    break;
                                case 'R':
                                    check = false;
                                    break;
                                case 'c':
                                    check = false;
                                    break;
                                case 'f':
                                    cursorHome(code);
                                    check = false;
                                    break;
                                case 'g':
                                    check = false;
                                    break;
                                case 'h':
                                    check = false;
                                    break;
                                case 'i':
                                    /** not support**/
                                    check = false;
                                    break;
                                case 'l':
                                    check = false;
                                    break;
                                case 'm':
                                    check = false;
                                    colorChange(code);
                                    break;
                                case 'n':
                                    check = false;
                                    break;
                                case 'p':
                                    check = false;
                                    break;
                                case 'r':
                                    check = false;
                                    break;
                                case 's':
                                    check = false;
                                    break;
                                case 'u':
                                    check = false;
                                    break;
                                default:
                                    code += font.getText() +"";
                            }
                        }
                    }else if(font != null &&(byte)font.getText() == (byte)'D' && !(font.isTwoBytes())){
                    }else if(font != null &&(byte)font.getText() == (byte)'M' && !(font.isTwoBytes())){
                    }else{
                        padding = true;
                    }
                }else if((byte)font.getText() == LF && !(font.isTwoBytes())){
                    row++;
                }else if((byte)font.getText() == CR && !(font.isTwoBytes())){
                    col = 0;
                }else{
                    if(col >= 80){
                        /** drop **/
                        continue;
                    }
                    
                    if((row >= 24) & !isSSH){
                        /** drop **/
                        continue;
                    }else if((row >= 300) & !isSSH){
                        for(int i = 0 ; i < screen.length ; i++){
                            screen[i] = null;
                        }
                        row = 0;
                    }
                    font.setBgcolor(bgcolor);
                    font.setFgcolor(fgcolor);
                    font.setType(type);
                    screen[row][col] = font;
                    col++;
                }
            }
            
            //printAll();
            terminal.setScreen(screen);
            terminal.keyLock();
            
            terminal.repaint();
        }catch(Exception e){
            e.printStackTrace();
        }
        
    }
    
    private void colorChange(String code){
        if( code.length() == 0 || code.equals("0")){
            bgcolor = 0x000000;
            fgcolor = 0xFFFFFF;
            type = 0;
            return;
        }else{
            StringTokenizer st = new StringTokenizer(code, ';');
            while(st.hasMoreTokens()){
                code = st.nextToken();
                int c = (code.length() > 0)? Integer.parseInt(code) : 0;
                switch(c){
                    case 30:
                        fgcolor = 0x575757;
                        break;
                    case 31:
                        fgcolor = 0xFF0000;
                        break;
                    case 32:
                        fgcolor = 0x00FF00;
                        break;
                    case 33:
                        fgcolor = 0xFFFF00;
                        break;
                    case 34:
                        fgcolor = 0x0000FF;
                        break;
                    case 35:
                        fgcolor = 0xFF00FF;
                        break;
                    case 36:
                        fgcolor = 0x00FFFF;
                        break;
                    case 37:
                        fgcolor = 0xFFFFFF;
                        break;
                    case 40:
                        bgcolor = 0x000000;
                        break;
                    case 41:
                        bgcolor = 0xFF0000;
                        break;
                    case 42:
                        bgcolor = 0x00FF00;
                        break;
                    case 43:
                        bgcolor = 0xFFFF00;
                        break;
                    case 44:
                        bgcolor = 0x0000FF;
                        break;
                    case 45:
                        bgcolor = 0xFF00FF;
                        break;
                    case 46:
                        bgcolor = 0x00FFFF;
                        break;
                    case 47:
                        bgcolor = 0xFFFFFF;
                        break;
                    case 7:
                        int temp = 0;
                        temp = bgcolor;
                        bgcolor = fgcolor;
                        fgcolor = temp;
                        break;
                        
                    default:
                        type = (code.length() != 0)? Integer.parseInt(code) : 0;
                        break;
                }
            }
        }
    }
    
    private void cursorHome(String code){
        if(code.equals(";") || code.length() == 0){
            row = 0;
            col = 0;
        }else{

            StringTokenizer st = new StringTokenizer(code,';');
            
            row = (st.hasMoreTokens())?Integer.parseInt(st.nextToken()) - 1 : 0;
            col = (st.hasMoreTokens())?Integer.parseInt(st.nextToken()) - 1 : 0;
        }
    }
    
    private void eraseK(String code){
        int kcode = (code.length() == 0)? 0 : Integer.parseInt(code);
        switch(kcode){
            case 0:
                for(int i = col ; i < screen[row].length ; i++){
                    screen[row][i] = null;
                }
                break;
            case 1:
                for(int i = col ; i >= 0 ; i--){
                    screen[row][i] = null;
                }
                break;
            case 2:
                screen[row] = null;
                break;
        }
    }
    private void eraseJ(String code){
        int jcode = (code.length() == 0)? 0 : Integer.parseInt(code);
        bgcolor = 0x000000;
        fgcolor = 0xFFFFFF;
        if(jcode == 2){
            row = 0;
            col = 0;
            for(int i = 0 ; i < screen.length ; i++){
                for(int j = 0 ; j < screen[i].length ; j++){
                    screen[i][j] = null;
                }
            }
            return;
        }else if(jcode == 1){
            for(int i = row ; i >= 0 ; i--){
                for(int j = col ; j >= 0 ;j--){
                    screen[i][j] = null;
                }
            }
        }else if(jcode == 0){
            for(int i = row; i < screen.length ; i++){
                for(int j = col ; j<screen[i].length; j++){
                    screen[i][j] = null;
                }
            }
        }
    }
    
    /** This method will return current cursor "X" position information. */
    public int getCursorPosX(){
        return col;
    }
    /** This method will return current cursor "Y" position information. */
    public int getCursorPosY(){
        return row;
    }
    
    
    
}
